from django import template
from django.conf import settings
from django.shortcuts import get_object_or_404, render_to_response
from django.http import HttpResponse, Http404, HttpResponseNotModified, HttpResponsePermanentRedirect, HttpResponseGone
from random import seed, randrange
from base64 import urlsafe_b64encode, urlsafe_b64decode
from rssproxy.feeds.models import Feed
from urllib2 import HTTPDigestAuthHandler, build_opener, install_opener, urlopen, Request, HTTPError
from lj import lj
import re
from hashlib import md5
from xml.dom.minidom import parse

feed_base = settings.FEED_BASE

def render_file_to_response(mimetype, filename, file):
    response = HttpResponse(mimetype=mimetype)
    response['Content-Disposition'] = 'attachment; filename=%s' % filename
    response.write(file)
    return response

# The index view
def index(request):
    return render_to_response('feeds/index.html', {'title': _('Feeds management')}, context_instance=template.RequestContext(request))

def key_gen(length):
    seed()
    a =  []
    for i in range(0, length):
        a.append(chr(randrange(0, 255)))
    return "".join(a)

def enc(username, password):
    creds = "%s %s " % (username, password)
    enc = []
    key = key_gen(40)
    # pad to the key length
    for i in range(0, len(key) - len(creds)):
        creds = creds + chr(randrange(0,255))
    # XOR the credentials with the key
    for key_char, plain_char in zip(key, creds):
        enc.append(chr(ord(plain_char) ^ ord(key_char)))
    enc_base64_str = urlsafe_b64encode("".join(enc))
    key_base64_str = urlsafe_b64encode(key)
    return enc_base64_str, key_base64_str

def dec(enc, key):
    key = urlsafe_b64decode(key)
    enc = urlsafe_b64decode(enc)
    dec = ''
    for key_char, enc_char in zip(key, enc):
        dec = dec + chr(ord(key_char) ^ ord(enc_char))
    creds = dec.split(' ', 2)
    if len(creds) < 2:
        raise Exception("Couldn't decrypt the key")
    return creds[0], creds[1]

def create_feed(url, username, password, only_protected, mark_protected):
    enc_creds, base64_key = enc(username, password)
    creds_hash = md5(username + password).hexdigest()

    # Create a row in the DB
    feed = Feed(
        encrypted_key=enc_creds, 
        url=url, 
        creds_hash=creds_hash, 
        only_protected=only_protected, 
        mark_protected=mark_protected
    )
    feed.save()

    # Create the URL
    auth_url = "%s%d/%s/" % (feed_base, feed.id, base64_key)
    return auth_url

def create_feed_url(user, obj):
    if obj['type'] == 'community':
        return 'http://community.livejournal.com/%s/data/atom' % user

    if re.match('^_', user) or re.search('_$', user):
        return 'http://users.livejournal.com/%s/data/atom' % user

    user = user.replace('_', '-')
    return 'http://%s.livejournal.com/data/atom' % user


# Create OPML of an LJ group, identified by the id. id = 0 corresponds to 'all friends', id = 1 - the default group.
def get_group_opml(username, password, id, only_protected, mark_protected):
    lj_obj = lj(username, password)
    str = """<?xml version='1.0'?>
<opml version='1.1'><head>
<title>An RSS Proxy generated OPML</title>
<ownerName>%s</ownerName>
<ownerEmail>%s.livejournal.com</ownerEmail>
</head><body>
""" % (username, username)
    for user, obj in lj_obj.get_group_members(int(id)).iteritems():
        title = obj['name']
        feed_url = create_feed_url(user, obj)
        auth_feed_url = create_feed(feed_url,username,password, only_protected, mark_protected)
        only_str = ""
        if only_protected:
            only_str = " [protected-only]"
        str = str + "<outline text=\"%s\" title=\"%s%s\" type=\"rss\" xmlUrl=\"%s\" htmlUrl=\"http://%s.livejournal.com/\"/>\n" % (title, title, only_str, auth_feed_url, user)
    str = str + "</body></opml>\n"
    return str

def get_bool_val(name, request):
    if name in request.POST and request.POST[name] != 'False':
        return True
    else:
        return False

# The Add view
def add(request):
    username = request.POST['username']
    password = request.POST['password']
    only_protected = get_bool_val('only_protected', request)
    mark_protected = get_bool_val('mark_protected', request)

    if 'group_choice' in request.POST:
        group_id = request.POST['group_choice']
        return render_file_to_response('text/xml', 'group_opml.xml', get_group_opml(username, password, group_id, only_protected, mark_protected))

    try:
        url = request.POST['url']
        group_choice = request.POST['group']
    except (KeyError):
        return render_to_response('feeds/index.html', {'error_message': 'Something went wrong with the request'})

    if group_choice == 'single':
        auth_url = create_feed(url, username, password, only_protected, mark_protected)
        return render_to_response('feeds/index.html', {'success_message': 'An authenticated feed for the URL "%s" with username "%s" and password "%s" was created successfully. Click to add it to the Google Reader: <a href="http://fusion.google.com/add?source=atgs&feedurl=%s"><img src="http://gmodules.com/ig/images/plus_google.gif" border="0" alt="Add to Google"></a>' % (url, username, password, auth_url)})

    if group_choice == 'all':
        return render_file_to_response('text/xml', 'all_friends_opml.xml', get_group_opml(username, password, 0, only_protected, mark_protected))

    lj_obj = lj(username, password)
    groups = lj_obj.get_groups_list()
    return render_to_response('feeds/groups_choice.html', {'title': _('Group choice'), 'groups': groups, 'username': username, 'password': password, 'only_protected': only_protected, 'mark_protected': mark_protected})

# The remove view - TODO
def remove(request):
    try:
        username = request.POST['username']
        password = request.POST['password']
    except (KeyError):
        return render_to_response('feeds/index.html', {'error_message': 'Something went wrong with the request'})
    creds_hash = md5(username+password).hexdigest()
    try:
        Feed.objects.filter(creds_hash=creds_hash).delete()
    except Exception:
        return render_to_response('feeds/index.html', {'error_message': 'There was an error deleting your feeds. Talk to the administrator.'})
    return render_to_response('feeds/index.html', {'success_message': 'The authenticated feeds for %s were removed successfully.' % (username)})

def get_text_node(node):
    for n in node[0].childNodes:
        if n.nodeType == n.TEXT_NODE:
            return n


def process_protected(f, only_protected, mark_protected):
    dom = parse(f)
    items = dom.getElementsByTagName('item')
    for item in items:
        security = get_text_node(item.getElementsByTagName('lj:security')).data
        if security == 'public' and only_protected:
            item.parentNode.removeChild(item)
            continue
        if security != 'public' and mark_protected:
        # Don't do anything about the nodes without any title (the proper way
        # to handle it is to actually add a title with [protected] in it)
            try:
                title_text = get_text_node(item.getElementsByTagName('title'))
                title_text.data = '[protected] ' + title_text.data
            except:
                pass
    return dom.toxml()

# The view that serves the feed
def serve_feed(request, feed_id, key):
    feed = get_object_or_404(Feed, id=feed_id)
    encrypted_key = feed.encrypted_key
    try: 
        username, password = dec(key, encrypted_key)
    except Exception:
        return HttpResponsePermanentRedirect('http://www.google.com/')
    url = feed.url
    url = url + "?auth=digest"

    # set up the auth data
    authinfo = HTTPDigestAuthHandler()
    authinfo.add_password('lj', url, username, password)
    opener = build_opener(authinfo)
    install_opener(opener)
    new_request = Request(url)
    # If there was an 'If-Modified-Since' in the original request, push it into a new one as well
    if 'HTTP_IF_MODIFIED_SINCE' in request.META:
        new_request.add_header('If-Modified-Since', request.META['HTTP_IF_MODIFIED_SINCE'])

    try:
        f = urlopen(new_request)
    except HTTPError, e:
        if e.code == 304:
            return HttpResponseNotModified()
        if e.code == 401:
            return HttpResponseGone()
        return HttpResponseGone("Server returnes %d" % e.code)

    if feed.only_protected or feed.mark_protected:
        resp = HttpResponse(process_protected(f, feed.only_protected, feed.mark_protected))
    else:
        resp = HttpResponse(f)
    info = f.info()
    if 'Last-Modified' in info:
        resp['Last-Modified'] = info['Last-Modified']
    return resp
